package edu.drexel.cs544.group11.server;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.math.BigInteger;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.Vector;

import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;

import edu.drexel.cs544.group11.common.MessageFormatException;
import edu.drexel.cs544.group11.common.Product;
import edu.drexel.cs544.group11.common.ProtocolState;
import edu.drexel.cs544.group11.common.RequestMessage;

/**
 * STATEFUL
 * 
 * CONCURRENT
 * 
 * A connection to the client that runs on a separate thread.
 * 
 * CS 544
 * 
 * @author Group 11:
 * @author Jason Setzer
 * @author Tam Thongsima
 * @author Kenny Oyefara
 * @author Chase Miller
 * 
 * @date Spring 2013
 * 
 */
public class ClientConnection extends Thread {
    
    private int id;
    private ProtocolState state; // STATEFUL
    private Server server;
    private Vector<Product> clientCart;
    
    private SSLSocket clientSocket;
    private BufferedReader inFromClientSocket;
    private PrintStream outToClientSocket;
    
    public ClientConnection(Server server, SSLSocket socket) throws IOException {
	this.server = server;
	this.clientSocket = socket;
	
	this.inFromClientSocket = new BufferedReader(new InputStreamReader(this.clientSocket.getInputStream()));
	this.outToClientSocket = new PrintStream(this.clientSocket.getOutputStream());
    }
    
    /**
     * CONCURRENT
     * 
     * Creates a new client connection and adds it to the server's list of
     * connections.
     * 
     * @param inFromClient
     * @param outToClient
     */
    @SuppressWarnings("deprecation")
    public void run() {
	System.out.println("Establishing new client connection...");
	
	SSLSession session = this.clientSocket.getSession();
	Certificate[] cchain2 = session.getLocalCertificates();
	for (int i = 0; i < cchain2.length; i++) {
	    System.out.println(((X509Certificate) cchain2[i]).getSubjectDN());
	}
	System.out.println("Peer host is " + session.getPeerHost());
	System.out.println("Cipher is " + session.getCipherSuite());
	System.out.println("Protocol is " + session.getProtocol());
	System.out.println("ID is " + new BigInteger(session.getId()));
	System.out.println("Session created in " + session.getCreationTime());
	System.out.println("Session accessed in " + session.getLastAccessedTime());
	
	this.server.sendSuccess(this);
	
	// CONCURRENT
	while (this.clientSocket.isConnected()) {
	    try {
		RequestMessage message = this.waitForRequest();
		this.server.handleRequest(this, message);
	    }
	    catch (IOException e) {
		//FIXME - If something is wrong with the socket, stop this thread.
		this.stop();
	    }
	    catch (MessageFormatException me) {
	    }
	}
    }
    
    /**
     * Waits for a request and returns a RequestMessage object.
     * 
     * @return
     */
    public RequestMessage waitForRequest() throws IOException, MessageFormatException {
	RequestMessage request = null;
	String fullMessage = "";
	
	while (!fullMessage.endsWith(RequestMessage.getEndOfMessage())) {
	    String nextLine = "";
	    try {
		if ((nextLine = this.inFromClientSocket.readLine()) != null) {
		    nextLine += "\n";
		    fullMessage += nextLine;
		}
		else {
		    this.stop();
		}
	    }
	    catch (Throwable t) {
		this.stop();
	    }
	}
	
	request = new RequestMessage(fullMessage);
	
	return request;
    }
    
    /**
     * Returns this connection's print stream.
     * 
     * @return
     */
    public PrintStream getPrintStream() {
	return this.outToClientSocket;
    }
    
    public BufferedReader getClientReader() {
	return this.inFromClientSocket;
    }
    
    /**
     * STATEFUL
     * 
     * @return
     */
    public ProtocolState getProtocolState() {
	return this.state;
    }
    
    /**
     * STATEFUL
     * 
     * @param state
     */
    public void setState(ProtocolState state) {
	this.state = state;
    }
    
    public boolean doesClientIdMatch(int clientId) {
	if (clientId == id)
	    return true;
	return false;
    }
    
    public int getClientId() {
	return this.id;
    }
    
    public void setClientId(int Id) {
	this.id = Id;
    }
    
    public SSLSocket getClientSocket() {
	return this.clientSocket;
    }
    
    /**
     * gets list of Product for this client
     * 
     * @return
     */
    public Vector<Product> getClientCart() {
	return clientCart;
    }
    
    /**
     * sets list of Product for this client
     * 
     * @param clientCart
     */
    public void setClientCart(Vector<Product> clientCart) {
	this.clientCart = clientCart;
    }
}
